package org.apache.commons.net.ntp; /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.net.DatagramPacket; /*** * Implementation of NtpV3Packet with methods converting Java objects to/from * the Network Time Protocol (NTP) data message header format described in RFC-1305. * * @author Naz Irizarry, MITRE Corp * @author Jason Mathews, MITRE Corp * * @version $Revision: 658518 $ $Date: 2008-05-21 02:04:30 +0100 (Wed, 21 May 2008) $ */ public class NtpV3Impl implements NtpV3Packet { private static final int MODE_INDEX = 0; private static final int MODE_SHIFT = 0; private static final int VERSION_INDEX = 0; private static final int VERSION_SHIFT = 3; private static final int LI_INDEX = 0; private static final int LI_SHIFT = 6; private static final int STRATUM_INDEX = 1; private static final int POLL_INDEX = 2; private static final int PRECISION_INDEX = 3; private static final int ROOT_DELAY_INDEX = 4; private static final int ROOT_DISPERSION_INDEX = 8; private static final int REFERENCE_ID_INDEX = 12; private static final int REFERENCE_TIMESTAMP_INDEX = 16; private static final int ORIGINATE_TIMESTAMP_INDEX = 24; private static final int RECEIVE_TIMESTAMP_INDEX = 32; private static final int TRANSMIT_TIMESTAMP_INDEX = 40; private static final int KEY_IDENTIFIER_INDEX = 48; private static final int MESSAGE_DIGEST = 54; /* len 16 bytes */ private byte[] buf = new byte[48]; private volatile DatagramPacket dp; /** Creates a new instance of NtpV3Impl */ public NtpV3Impl() { } /*** * Returns mode as defined in RFC-1305 which is a 3-bit integer * whose value is indicated by the MODE_xxx parameters. * * @return mode as defined in RFC-1305. */ public int getMode() { return (ui(buf[MODE_INDEX]) >> MODE_SHIFT) & 0x7; } /*** * Return human-readable name of message mode type as described in * RFC 1305. * @return mode name as string. */ public String getModeName() { return NtpUtils.getModeName(getMode()); } /*** * Set mode as defined in RFC-1305. * @param mode */ public void setMode(int mode) { buf[MODE_INDEX] = (byte) (buf[MODE_INDEX] & 0xF8 | mode & 0x7); } /*** * Returns leap indicator as defined in RFC-1305 which is a two-bit code: * 0=no warning * 1=last minute has 61 seconds * 2=last minute has 59 seconds * 3=alarm condition (clock not synchronized) * * @return leap indicator as defined in RFC-1305. */ public int getLeapIndicator() { return (ui(buf[LI_INDEX]) >> LI_SHIFT) & 0x3; } /*** * Set leap indicator as defined in RFC-1305. * @param li leap indicator. */ public void setLeapIndicator(int li) { buf[LI_INDEX] = (byte) (buf[LI_INDEX] & 0x3F | ((li & 0x3) << LI_SHIFT)); } /*** * Returns poll interval as defined in RFC-1305, which is an eight-bit * signed integer indicating the maximum interval between successive * messages, in seconds to the nearest power of two (e.g. value of six * indicates an interval of 64 seconds. The values that can appear in * this field range from NTP_MINPOLL to NTP_MAXPOLL inclusive. * * @return poll interval as defined in RFC-1305. */ public int getPoll() { return buf[POLL_INDEX]; } /*** * Set poll interval as defined in RFC-1305. * * @param poll poll interval. */ public void setPoll(int poll) { buf[POLL_INDEX] = (byte) (poll & 0xFF); } /*** * Returns precision as defined in RFC-1305 encoded as an 8-bit signed * integer (seconds to nearest power of two). * Values normally range from -6 to -20. * * @return precision as defined in RFC-1305. */ public int getPrecision() { return buf[PRECISION_INDEX]; } /*** * Set precision as defined in RFC-1305. * @param precision */ public void setPrecision(int precision) { buf[PRECISION_INDEX] = (byte) (precision & 0xFF); } /*** * Returns NTP version number as defined in RFC-1305. * * @return NTP version number. */ public int getVersion() { return (ui(buf[VERSION_INDEX]) >> VERSION_SHIFT) & 0x7; } /*** * Set NTP version as defined in RFC-1305. * * @param version NTP version. */ public void setVersion(int version) { buf[VERSION_INDEX] = (byte) (buf[VERSION_INDEX] & 0xC7 | ((version & 0x7) << VERSION_SHIFT)); } /*** * Returns Stratum as defined in RFC-1305, which indicates the stratum level * of the local clock, with values defined as follows: 0=unspecified, * 1=primary ref clock, and all others a secondary reference (via NTP). * * @return Stratum level as defined in RFC-1305. */ public int getStratum() { return ui(buf[STRATUM_INDEX]); } /*** * Set stratum level as defined in RFC-1305. * * @param stratum stratum level. */ public void setStratum(int stratum) { buf[STRATUM_INDEX] = (byte) (stratum & 0xFF); } /*** * Return root delay as defined in RFC-1305, which is the total roundtrip delay * to the primary reference source, in seconds. Values can take positive and * negative values, depending on clock precision and skew. * * @return root delay as defined in RFC-1305. */ public int getRootDelay() { return getInt(ROOT_DELAY_INDEX); } /*** * Return root delay as defined in RFC-1305 in milliseconds, which is * the total roundtrip delay to the primary reference source, in * seconds. Values can take positive and negative values, depending * on clock precision and skew. * * @return root delay in milliseconds */ public double getRootDelayInMillisDouble() { double l = getRootDelay(); return l / 65.536; } /*** * Returns root dispersion as defined in RFC-1305. * @return root dispersion. */ public int getRootDispersion() { return getInt(ROOT_DISPERSION_INDEX); } /*** * Returns root dispersion (as defined in RFC-1305) in milliseconds. * * @return root dispersion in milliseconds */ public long getRootDispersionInMillis() { long l = getRootDispersion(); return (l * 1000) / 65536L; } /*** * Returns root dispersion (as defined in RFC-1305) in milliseconds * as double precision value. * * @return root dispersion in milliseconds */ public double getRootDispersionInMillisDouble() { double l = getRootDispersion(); return l / 65.536; } /*** * Set reference clock identifier field with 32-bit unsigned integer value. * See RFC-1305 for description. * * @param refId reference clock identifier. */ public void setReferenceId(int refId) { for (int i = 3; i >= 0; i--) { buf[REFERENCE_ID_INDEX + i] = (byte) (refId & 0xff); refId >>>= 8; // shift right one-byte } } /*** * Returns the reference id as defined in RFC-1305, which is * a 32-bit integer whose value is dependent on several criteria. * * @return the reference id as defined in RFC-1305. */ public int getReferenceId() { return getInt(REFERENCE_ID_INDEX); } /*** * Returns the reference id string. String cannot be null but * value is dependent on the version of the NTP spec supported * and stratum level. Value can be an empty string, clock type string, * IP address, or a hex string. * * @return the reference id string. */ public String getReferenceIdString() { int version = getVersion(); int stratum = getStratum(); if (version == VERSION_3 || version == VERSION_4) { if (stratum == 0 || stratum == 1) { return idAsString(); // 4-character ASCII string (e.g. GPS, USNO) } // in NTPv4 servers this is latest transmit timestamp of ref source if (version == VERSION_4) return idAsHex(); } // Stratum 2 and higher this is a four-octet IPv4 address // of the primary reference host. if (stratum >= 2) { return idAsIPAddress(); } return idAsHex(); } /*** * Returns Reference id as dotted IP address. * @return refId as IP address string. */ private String idAsIPAddress() { return ui(buf[REFERENCE_ID_INDEX]) + "." + ui(buf[REFERENCE_ID_INDEX + 1]) + "." + ui(buf[REFERENCE_ID_INDEX + 2]) + "." + ui(buf[REFERENCE_ID_INDEX + 3]); } private String idAsString() { StringBuilder id = new StringBuilder(); for (int i = 0; i <= 3; i++) { char c = (char) buf[REFERENCE_ID_INDEX + i]; if (c == 0) break; // 0-terminated string id.append(c); } return id.toString(); } private String idAsHex() { return Integer.toHexString(getReferenceId()); } /*** * Returns the transmit timestamp as defined in RFC-1305. * * @return the transmit timestamp as defined in RFC-1305. * Never returns a null object. */ public TimeStamp getTransmitTimeStamp() { return getTimestamp(TRANSMIT_TIMESTAMP_INDEX); } /*** * Set transmit time with NTP timestamp. * If <code>ts</code> is null then zero time is used. * * @param ts NTP timestamp */ public void setTransmitTime(TimeStamp ts) { setTimestamp(TRANSMIT_TIMESTAMP_INDEX, ts); } /*** * Set originate timestamp given NTP TimeStamp object. * If <code>ts</code> is null then zero time is used. * * @param ts NTP timestamp */ public void setOriginateTimeStamp(TimeStamp ts) { setTimestamp(ORIGINATE_TIMESTAMP_INDEX, ts); } /*** * Returns the originate time as defined in RFC-1305. * * @return the originate time. * Never returns null. */ public TimeStamp getOriginateTimeStamp() { return getTimestamp(ORIGINATE_TIMESTAMP_INDEX); } /*** * Returns the reference time as defined in RFC-1305. * * @return the reference time as <code>TimeStamp</code> object. * Never returns null. */ public TimeStamp getReferenceTimeStamp() { return getTimestamp(REFERENCE_TIMESTAMP_INDEX); } /*** * Set Reference time with NTP timestamp. If <code>ts</code> is null * then zero time is used. * * @param ts NTP timestamp */ public void setReferenceTime(TimeStamp ts) { setTimestamp(REFERENCE_TIMESTAMP_INDEX, ts); } /*** * Returns receive timestamp as defined in RFC-1305. * * @return the receive time. * Never returns null. */ public TimeStamp getReceiveTimeStamp() { return getTimestamp(RECEIVE_TIMESTAMP_INDEX); } /*** * Set receive timestamp given NTP TimeStamp object. * If <code>ts</code> is null then zero time is used. * * @param ts timestamp */ public void setReceiveTimeStamp(TimeStamp ts) { setTimestamp(RECEIVE_TIMESTAMP_INDEX, ts); } /*** * Return type of time packet. The values (e.g. NTP, TIME, ICMP, ...) * correspond to the protocol used to obtain the timing information. * * @return packet type string identifier which in this case is "NTP". */ public String getType() { return "NTP"; } /*** * @return 4 bytes as 32-bit int */ private int getInt(int index) { int i = ui(buf[index]) << 24 | ui(buf[index + 1]) << 16 | ui(buf[index + 2]) << 8 | ui(buf[index + 3]); return i; } /*** * Get NTP Timestamp at specified starting index. * * @param index index into data array * @return TimeStamp object for 64 bits starting at index */ private TimeStamp getTimestamp(int index) { return new TimeStamp(getLong(index)); } /*** * Get Long value represented by bits starting at specified index. * * @return 8 bytes as 64-bit long */ private long getLong(int index) { long i = ul(buf[index]) << 56 | ul(buf[index + 1]) << 48 | ul(buf[index + 2]) << 40 | ul(buf[index + 3]) << 32 | ul(buf[index + 4]) << 24 | ul(buf[index + 5]) << 16 | ul(buf[index + 6]) << 8 | ul(buf[index + 7]); return i; } /*** * Sets the NTP timestamp at the given array index. * * @param index index into the byte array. * @param t TimeStamp. */ private void setTimestamp(int index, TimeStamp t) { long ntpTime = (t == null) ? 0 : t.ntpValue(); // copy 64-bits from Long value into 8 x 8-bit bytes of array // one byte at a time shifting 8-bits for each position. for (int i = 7; i >= 0; i--) { buf[index + i] = (byte) (ntpTime & 0xFF); ntpTime >>>= 8; // shift to next byte } // buf[index] |= 0x80; // only set if 1900 baseline.... } /*** * Returns the datagram packet with the NTP details already filled in. * * @return a datagram packet. */ public synchronized DatagramPacket getDatagramPacket() { if (dp == null) { dp = new DatagramPacket(buf, buf.length); dp.setPort(NTP_PORT); } return dp; } /*** * Set the contents of this object from source datagram packet. * * @param srcDp source DatagramPacket to copy contents from. */ public void setDatagramPacket(DatagramPacket srcDp) { byte[] incomingBuf = srcDp.getData(); int len = srcDp.getLength(); if (len > buf.length) len = buf.length; System.arraycopy(incomingBuf, 0, buf, 0, len); } /*** * Convert byte to unsigned integer. * Java only has signed types so we have to do * more work to get unsigned ops. * * @param b * @return unsigned int value of byte */ protected final static int ui(byte b) { int i = b & 0xFF; return i; } /*** * Convert byte to unsigned long. * Java only has signed types so we have to do * more work to get unsigned ops * * @param b * @return unsigned long value of byte */ protected final static long ul(byte b) { long i = b & 0xFF; return i; } /*** * Returns details of NTP packet as a string. * * @return details of NTP packet as a string. */ @Override public String toString() { return "[" + "version:" + getVersion() + ", mode:" + getMode() + ", poll:" + getPoll() + ", precision:" + getPrecision() + ", delay:" + getRootDelay() + ", dispersion(ms):" + getRootDispersionInMillisDouble() + ", id:" + getReferenceIdString() + ", xmitTime:" + getTransmitTimeStamp().toDateString() + " ]"; } }